Existen varias formas de añadir un menú a una ventana, veremos cada una de ellas por separado.
Es el sistema más rudimentario, pero en ocasiones puede ser muy útil. Este sistema ilustra mucho mejor la estructura de los menús.
#define CM_PRUEBA 100 // constante ID del menú PRUEBA #define CM_SALIR 101 // constante ID del menú SALIR (...) void InsertarMenu(HWND); // prototipo
#define CM_PRUEBA 100 // constante ID del menú PRUEBA
#define CM_SALIR 101 // constante ID del menú SALIR
(...)
void InsertarMenu(HWND); // prototipoAl final del programa:
(...)
void InsertarMenu(HWND hWnd)
{
HMENU hBarraMenus;
HMENU hMenuPopup;
hBarraMenus = CreateMenu(); // Crea el manipulador de la barra de menús
hMenuPopup = CreateMenu(); // Crea el manipulador del primer menú pop-up
// añade los elementos tipo cadena y/o separador al menú pop-up
AppendMenu(hMenuPopup, MF_STRING, CM_PRUEBA, “&Prueba”); // 1er ítem
AppendMenu(hMenuPopup, MF_SEPARATOR, 0, NULL); // 2º ítem (separador)
AppendMenu(hMenuPopup, MF_STRING, CM_SALIR, “&Salir”); // 3er ítem
// Inserta el menú pop-up en la barra de menús
AppendMenu(hBarraMenus, MF_STRING | MF_POPUP, (UINT)hMenuPopup, “&Principal”);
// Inserta la barra de menús en la ventana
SetMenu (hWnd, hBarraMenus);
}(...)
void InsertarMenu(HWND hWnd)
{
HMENU hBarraMenus;
HMENU hMenuPopup;
hBarraMenus = CreateMenu(); // Crea el manipulador de la barra de menús
hMenuPopup = CreateMenu(); // Crea el manipulador del primer menú pop-up
// añade los elementos tipo cadena y/o separador al menú pop-up
AppendMenu(hMenuPopup, MF_STRING, CM_PRUEBA, “&Prueba”); // 1er ítem
AppendMenu(hMenuPopup, MF_SEPARATOR, 0, NULL); // 2º ítem (separador)
AppendMenu(hMenuPopup, MF_STRING, CM_SALIR, “&Salir”); // 3er ítem
// Inserta el menú pop-up en la barra de menús
AppendMenu(hBarraMenus, MF_STRING | MF_POPUP, (UINT)hMenuPopup, “&Principal”);
// Inserta la barra de menús en la ventana
SetMenu (hWnd, hBarraMenus);
}Y por último, sólo nos queda llamar a nuestra función, insertaremos ésta llamada justo antes de visualizar la ventana.
InsertarMenu(hWnd); ShowWindow(hWnd, SW_SHOWDEFAULT);
InsertarMenu(hWnd); ShowWindow(hWnd, SW_SHOWDEFAULT);HMENU es un tipo de manipulador especial para menús. Necesitamos dos uno para el menú horizontal (barra de menús) y otro para el menú desplegable.
La función CreateMenu crea un menú vacío.
La función AppendMenu añade elementos uno a uno indicando, el menú donde hacer la inserción y las opciones del nuevo elemento: MF_STRING, indica que se trata de un ítem de tipo texto, MF_SEPARATOR, es un ítem separador y MF_POPUP, indica que se trata de un menú que desplegará un nuevo menú pop-up. El tercer parámetro puede ser un identificador de comando, este identificador se usará para comunicar a la aplicación si el usuario selecionó un determinado ítem; o puede ser un manipulador de menú, si el ítem tiene el flag MF_POPUP, en este caso hay que hacer un casting a (UINT); o también puede ser cero, si se trata de un separador. El último parámetro es el texto del ítem, cuando se ha especificado el flag MF_STRING, más adelante veremos que los ítems pueden ser también bitmaps. Normalmente se trata de una cadena de texto con el & para indicar la tecla de atajo.
Por último SetMenu asigna un menú a una ventana determinada. El primer parámetro es el manipulador de la ventana, y el segundo el del menú.
Las acciones sobre los menús se reciben como mensajes WM_COMMAND
Ese mensaje tiene tres argumentos de los cuales sólo nos interesa el segundo
wNotifyCode = HIWORD(wParam); // código de notificación wID = LOWORD(wParam); // identificador de ítem, control, o acelerador hwndCtl = (HWND) lParam; // manipulador de control
wNotifyCode = HIWORD(wParam); // código de notificación
wID = LOWORD(wParam); // identificador de ítem, control, o acelerador
hwndCtl = (HWND) lParam; // manipulador de controlPara sacar el identificador usaremos la macro LOWORD. Cambiamos el código de la función de procedimiento de ventana:
/* Esta función es invocada por la función DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
switch (mensaje) // manipulador de mensaje
{
case WM_COMMAND: // menús, un control envía un mensaje de notificación a su ventana padre, o una tecla atajo
switch(LOWORD(wParam)) // saca el low word (identificador de ítem, control, o acelerador) del mensaje
{
case CM_PRUEBA:
MessageBox(hwnd, "Comando: Prueba", "Mensaje de menú", MB_OK);
break;
case CM_SALIR:
MessageBox(hwnd, "Comando: Salir", "Mensaje de menú", MB_OK);
PostQuitMessage(0); // envía el mensaje WM_QUIT a la cola de mensajes
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0); // envía el mensaje WM_QUIT a la cola de mensajes
break;
default: // Mensajes que no queremos manejar y lo hará el sistema operativo
return DefWindowProc (hwnd, mensaje, wParam, lParam);
}
return 0;
}/* Esta función es invocada por la función DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam)
{
switch (mensaje) // manipulador de mensaje
{
case WM_COMMAND: // menús, un control envía un mensaje de notificación a su ventana padre, o una tecla atajo
switch(LOWORD(wParam)) // saca el low word (identificador de ítem, control, o acelerador) del mensaje
{
case CM_PRUEBA:
MessageBox(hwnd, "Comando: Prueba", "Mensaje de menú", MB_OK);
break;
case CM_SALIR:
MessageBox(hwnd, "Comando: Salir", "Mensaje de menú", MB_OK);
PostQuitMessage(0); // envía el mensaje WM_QUIT a la cola de mensajes
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0); // envía el mensaje WM_QUIT a la cola de mensajes
break;
default: // Mensajes que no queremos manejar y lo hará el sistema operativo
return DefWindowProc (hwnd, mensaje, wParam, lParam);
}
return 0;
}La forma más habitual de hacer menús: crear un fichero de recursos en el que se declaran los menús.
Creamos un archivo de cabecera “ids.h” y añadimos las constantes de los IDs de los menús:
#define CM_PRUEBA 100 #define CM_SALIR 101
#define CM_PRUEBA 100
#define CM_SALIR 101Siempre use identificadores para los ítems de los menús, nunca valores numéricos.
Incluimos ese archivo en el “win003.c” con #include “ids.h” justo después de incluir la cabecera de windows:
#include <windows.h> #include "ids.h"
#include <windows.h>
#include "ids.h"Creamos el archivo rc y lo añadimos al proyecto. La primera línea ha de incluir la cabecera de los identificadores
#include "ids.h"
BarraMenus MENU
BEGIN
POPUP "&Principal"
BEGIN
MENUITEM "&Prueba", CM_PRUEBA
MENUITEM SEPARATOR
MENUITEM "&Salir", CM_SALIR
END
END#include "ids.h"
BarraMenus MENU
BEGIN
POPUP "&Principal"
BEGIN
MENUITEM "&Prueba", CM_PRUEBA
MENUITEM SEPARATOR
MENUITEM "&Salir", CM_SALIR
END
ENDEste ejemplo crea una barra de menú con una columna “Principal”, con dos opciones: “Prueba” y “Salir”, y con un separador entre ellas.
Definimos el menú mediante una cadena identificadora, sin comillas, seguida de la palabra MENU. Entre las palabras BEGIN y END podemos incluir items, separadores u otras columnas. Para incluir columas usamos una sentencia del tipo POPUP seguida de la cadena que se mostrará como texto en el menú. Cada POPUP se comporta del mismo modo que un MENU.
Los ítems se crean usado la palabra MENUITEM seguida de la cadena que se mostrará en el menú, una coma, y el comando asignado a ese ítem, como una macro definida.
Los separadores se crean usando MENUITEM SEPARATOR.
SetMenu(hWnd, LoadMenu(hInstance, "BarraMenus")); ShowWindow(hWnd);
SetMenu(hWnd, LoadMenu(hInstance, "BarraMenus"));
ShowWindow(hWnd);WNDCLASSEX wincl; (...) wincl.lpszMenuName = "BarraMenus";
WNDCLASSEX wincl;
(...)
wincl.lpszMenuName = "BarraMenus"; hWnd = CreateWindowEx(
0,
"NUESTRA_CLASE",
"Ejemplo 003",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
544,
375,
HWND_DESKTOP,
LoadMenu(hInstance, "BarraMenus"), /* Carga y asignación del menú */
hInstance,
NULL
);hWnd = CreateWindowEx(
0,
"NUESTRA_CLASE",
"Ejemplo 003",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
544,
375,
HWND_DESKTOP,
LoadMenu(hInstance, "BarraMenus"), /* Carga y asignación del menú */
hInstance,
NULL
);